package cn.ddiancan.xddcloud.psc.audit.aspect;

import cn.ddiancan.xddcloud.common.context.RequestContext;
import cn.ddiancan.xddcloud.common.entity.UserVO;
import cn.ddiancan.xddcloud.psc.audit.annotation.AuditLog;
import cn.ddiancan.xddcloud.psc.audit.annotation.Sensitive;
import cn.ddiancan.xddcloud.psc.audit.domain.AuditLogVO;
import cn.ddiancan.xddcloud.psc.audit.service.AuditClientService;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.Objects;

@Slf4j
@Aspect
@Service
public class AuditAspect {

    private static final ThreadLocal<AuditLogVO> threadLocal = new ThreadLocal<>();

    @Autowired
    private AuditClientService auditClientService;

    @Pointcut("@annotation(cn.ddiancan.xddcloud.psc.audit.annotation.AuditLog)")
    public void pointCut() {
    }


    @Around("pointCut()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        UserVO currentUser = RequestContext.getCurrentContextUser();
        AuditLogVO auditLogVO = new AuditLogVO();
        Date date = new Date();
        auditLogVO.setCreateDate(date);
        auditLogVO.setLastUpdateDate(date);
        if (Objects.nonNull(currentUser)) {
            auditLogVO.setUserId(String.valueOf(currentUser.getUserId()));
            auditLogVO.setCreatedBy(currentUser.getUserId());
            auditLogVO.setLastUpdateBy(currentUser.getUserId());
        }
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        AuditLog auditLog = method.getAnnotation(AuditLog.class);
        if (auditLog != null) {
            auditLogVO.setOperationCode(auditLog.value());
            auditLogVO.setOperationDescription(auditLog.description());
        }
        auditLogVO.setExecuteMethod(method.getName());
        auditLogVO.setExecuteTime(date);
        auditLogVO.setFinishTime(new Date());
        auditLogVO.setParameters(joinPoint.getArgs());
        threadLocal.set(auditLogVO);
        Object result = joinPoint.proceed();
        auditLogVO.setResult(result);
        Object[] parameters = auditLogVO.getParameters();
        for (Object arg : parameters) {
            if (arg != null) {
                handleSensitiveFields(arg.getClass(), arg);
            }
        }
        log.info("切面获取日期数据:");
        log.info("insert auditLogVO:{}", JSON.toJSONString(auditLogVO));
        log.info("insert getCreateDate:{}", auditLogVO.getCreateDate());
        log.info("insert getLastUpdateDate:{}", auditLogVO.getLastUpdateDate());
        log.info("insert getExecuteTime:{}", auditLogVO.getExecuteTime());
        log.info("insert getFinishTime:{}", auditLogVO.getFinishTime());
        auditClientService.addSystemLog(auditLogVO);
        return result;
    }

    @AfterThrowing(value = "pointCut()", throwing = "exception")
    public void doAfterThrowing(Exception exception) {
        AuditLogVO auditLogVO = threadLocal.get();
        if (Objects.nonNull(auditLogVO)) {
            auditLogVO.setFinishTime(new Date());
            auditLogVO.setResult(exception.getMessage());
            auditClientService.addSystemLog(auditLogVO);
        }
    }

    private void handleSensitiveFields(Class<?> clazz, Object obj) {
        Field[] fields = clazz.getDeclaredFields();
        try {
            for (Field field : fields) {
                if (field.isAnnotationPresent(Sensitive.class)) {
                    field.setAccessible(true);
                    field.set(obj, "*****"); // 加此注解的都替换为“*****”
                } else if (!field.getType().isPrimitive() && field.getType().getPackage() != null &&
                    !field.getType().getPackage().getName().startsWith("java.")) {
                    handleSensitiveFields(field.getType(), field.get(obj));
                }
            }
        } catch (IllegalAccessException e) {
            log.error("处理敏感字段异常:{}", e.getMessage());
        }
    }
}
